package com.summer.socket.im;

import java.time.LocalDateTime;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.concurrent.GlobalEventExecutor;

// 在这之前我们说对于 WebSocket 来讲，都是以 frames 进行传输的，所以这里我们使用 TextWebSocketFrame。处理消息的类型取决管道中上一个 Handler 如何处理
// TextWebSocketFrame：在 netty 中，是用于为 webSocket 专门处理文本的对象，Frame 事消息的载体。
public class ChatHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
	
	// netty 为我们提供的一个用于记录和管理所有客户端的 channel，它可以把相关的 channel 都保存到同一个 Group 组里面。
	// GlobalEventExecutor.INSTANCE 一个全局的 Executor
	private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

	@Override
	protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
		String content = msg.text();
		System.out.println("接受的数据：" + content);
		
		// 将收到的消息转发到所有用户手里，那么就需要记录下所有的用户，需要通过实现 handlerAdded 和 handlerRemoved 来完成添加和删除
		// 第一种方式回刷消息：使用遍历
//		for(Channel channel : channelGroup) {
//			// 关于 writeAndFlush 可以写一个对象，但是我们不能直接把 String 直接写入到这里，因为我们使用的是 WebSocket 所以我们的载体是 Frame，这里也需要使用 Frame。
//			// 这个问题和之前的 HTTP 案例一样，com.summer.socket.http.HttpHandler 也是不能写其他对象需要一个 FullHttpResponse。
//			// 之所以造成这个问题是因为，一、socket 通讯本就是基于字符流缓存的，所以直接写String会有问题。二、由于我们添加了很多针对 WebSocket 的 netty 自带处理器，
//			// 比如，WebSocketServerProtocolHandler，它也会对数据进行一些处理操作，由于管道是一个责任链模式，入站是从前向后处理，出站是从后向前处理，所以会回归到上一个handler再次被加工处理。
//			// 此时如果数据类型不对就会导致异常出现。
////			channel.writeAndFlush(msg);
//			channel.writeAndFlush(new TextWebSocketFrame("[服务器收到消息，" + LocalDateTime.now() +"]:" + content));
//		}
		// 第二种方式回刷消息：使用 ChannelGroup 提供的方法。两个方法差不多
		channelGroup.writeAndFlush(new TextWebSocketFrame("[服务器收到消息，" + LocalDateTime.now() +"]:" + content));
	}
	
	@Override
	public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
		// 在连接进入时获取所有客户端的 channel
		channelGroup.add(ctx.channel());
	}
	
	@Override
	public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
		// 在连接移除时移除所有的客户端的 channel
		// 实际上这个操作是多余的，当触发 handlerRemoved（也就是我们的连接关闭时），ChannelGroup 会自动移除对应的客户端 channel。（也解释了为什么不用普通容器来管理）
		channelGroup.remove(ctx.channel());
		System.out.println("客户端断开：长ID" + ctx.channel().id().asLongText() +"；短Id：" + ctx.channel().id().asShortText());
	}

}
